/* ***** BEGIN LICENSE BLOCK *****
 *   Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 * 
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is JsWs.
 *
 * The Initial Developer of the Original Code is
 * Chris Winberry.
 * Portions created by the Initial Developer are Copyright (C) 2007
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 * 
 * ***** END LICENSE BLOCK ***** */


/********************************************************************************
 * JsWsConnectionHandler
 *******************************************************************************/

/**
 * @class Handles an incoming socket connection
 * @constructor
 * @param {JsWs} server JsWs server socket listener
 * @param {nsIServerSocket} socket Incoming accepted socket
 * @param {nsISocketTransport} transport Socket transport
 */
function JsWsConnectionHandler (server, socket, transport) {
	/**	
	 * JsWs server socket listener
	 * @type JsWs
	 */
	this.server = server;
	/**	
	 * Incoming accepted socket
	 * @type nsIServerSocket
	 */
	this.socket = socket;
	/**	
	 * Socket transport
	 * @type nsISocketTransport
	 */
	this.transport = transport;
	/**	
	 * Request object
	 * @type Request
	 */
	this.request = new Request();
	/**	
	 * Response object
	 * @type Response
	 */
	this.response = new Response(this);

	if (transport) {
		/** @private */
		this.outstream = this.transport.openOutputStream(1, 0, 0);
		/** @private */
		this.stream = this.transport.openInputStream(0, 0, 0);
		/** @private */
		this.instream = Cc["@mozilla.org/binaryinputstream;1"].
			createInstance(Ci.nsIBinaryInputStream);
		this.instream.setInputStream(this.stream);
		/** @private */
		this.dataPump = Components.
			classes["@mozilla.org/network/input-stream-pump;1"].
			createInstance(Ci.nsIInputStreamPump);
		this.dataPump.init(this.stream, -1, -1, 0, 0, false);
		this.dataPump.asyncRead(this, null);
	}
}


/**
 * Runs cleanup on internal objects
 */
JsWsConnectionHandler.prototype.destroy = function () {
	this.server = null;
	this.socket = null;
	this.transport = null;
	if (this.outstream)
		this.outstream.close();
	this.outstream = null;
	if (this.request)
		this.request.destroy();
	this.request = null;
	if (this.response)
		this.response.destroy();
	this.response = null;
	if (this.instream)
		this.instream.close();
	this.instream = null;
	if (this.stream)
		this.stream.close();
	this.stream = null;
	this.dataPump = null;
}


/** @private */
JsWsConnectionHandler.prototype.onStartRequest = function (request, context) { }

/** @private */
JsWsConnectionHandler.prototype.onStopRequest = function (request, context, status) {
	this.destroy();
}


/**
 * Writes the data to the connected socket's output stream
 * @param {String} data Data to write to the stream
 */
JsWsConnectionHandler.prototype.write = function (data) {
	var realSize = data.length;
	var writeSize = this.outstream.write(data, data.length);
}


/** @private */
JsWsConnectionHandler.prototype.handleRequest = function () {
	var script = new ScriptContext(this.server, this.request.path);
	var codeHost = new EvalCodeHost(this.server, this.request, this.response, script);

	try {
		try {
			var mappedModule = this.server.mapModule(script.path.path);
			if (!mappedModule.handler)
				mappedModule = this.server.modules[JSWS_MODULES_ROOTKEY];
			codeHost.require(
				mappedModule.virtualPath +
				mappedModule.handler
				);
		} catch (ex) {
			if (!(ex instanceof ExResponseEnd)) {
				var _ex = (ex instanceof ExBase) ? ex : new ExBase("Uncaught top-level", 0, ex);
				this.response.status = 500;
				this.response.clear();
				this.response.contentType("text/html");
				this.response.write("<hr><pre>Server Error: " + _ex + "</pre><hr>");
			}
		}
		this.response.end();
	} catch (e) { }

	codeHost.destroy();
	codeHost = null;
	script.destroy();
	script = null;
}


/** @private */
JsWsConnectionHandler.prototype.onDataAvailable = function (request, context, inputStream, offset, count) {
	try {
		// TODO: Handle chunked input from body (uploads)
		if (this.request.parse(this.instream.readBytes(this.instream.available()))) {
			this.handleRequest();
			this.instream.close();
		}
	} catch (e) { JsWs.log(e); }
}

